package fr.uparis10.miage.m1p1.employes;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import fr.uparis10.miage.classic.m1.p3.Departement;
import fr.uparis10.miage.m1p1.core.Projet;
import fr.uparis10.miage.m1p1.global.BudgetReasonDirDeptEnum;

public class DirecteurDepartement extends Manager {

	/*
	 * The next attribute will just hold the maximum allowed budget for a
	 * specific application
	 */
	private static final HashMap<BudgetReasonDirDeptEnum, Double> MAX_BUDGET;
	static {
		MAX_BUDGET = new HashMap<BudgetReasonDirDeptEnum, Double>();
		MAX_BUDGET.put(BudgetReasonDirDeptEnum.PROJECT, (double) 100000);
		MAX_BUDGET.put(BudgetReasonDirDeptEnum.TEAM, (double) 10000);
		MAX_BUDGET.put(BudgetReasonDirDeptEnum.RELOCATION, (double) 20000);
		MAX_BUDGET.put(BudgetReasonDirDeptEnum.TRAVEL, (double) 30000);
		MAX_BUDGET.put(BudgetReasonDirDeptEnum.OTHER, (double) 1000);
	}

	/* This variable will hold the history of budget allocation */
	private HashMap<BudgetReasonDirDeptEnum, Double> mUsedBudget = new HashMap<BudgetReasonDirDeptEnum, Double>();

	/* Constructors - START */
	public DirecteurDepartement() {
		super();
	}

	public DirecteurDepartement(int preferedEmplId, long preferedNumSS) {
		super(preferedEmplId, preferedNumSS);
	}

	public DirecteurDepartement(int preferedEmplId, long preferedNumSS,
			Date joinDate) {
		super(preferedEmplId, preferedNumSS, joinDate);
	}

	public DirecteurDepartement(int preferedEmplId, long preferedNumSS,
			Date joinDate, double budget, Date dateNomination) {
		super(preferedEmplId, preferedNumSS, joinDate, budget, dateNomination);
	}

	/* Constructors - END */

	/**
	 * This static method is used to exchange the department for 2 Directeurs
	 * 
	 * @param dir1
	 * @param dir2
	 * @return
	 */
	private static boolean exchangeDepartments(DirecteurDepartement dir1,
			DirecteurDepartement dir2) {
		if (dir1 == null || dir2 == null) {
			return false;
		}

		if (dir1.departement != null) {
			// remove from Department Employe list
			dir1.departement.removeEmploye(dir1);
			// add to the department Employe list
			dir1.departement.addEmploye(dir2);
		}

		if (dir2.departement != null) {
			// remove from Department Employe list
			dir2.departement.removeEmploye(dir2);
			// add to the department Employe list
			dir2.departement.addEmploye(dir1);
		}

		// exchange the departments
		Departement auxDept = dir1.departement;
		dir1.departement = dir2.departement;
		dir2.departement = auxDept;

		return true;
	}

	/**
	 * We'll use this method to ask a YES/NO question on the console
	 * 
	 * @param msg
	 * @return
	 */
	private boolean askYesNo(String msg) {
		String resp;
		do {
			System.out.print(msg);
			byte[] buffer = new byte[100];
			try {
				System.in.read(buffer);
			} catch (IOException e) {
				// An input exception occurred; print it and disapprove
				e.printStackTrace();
				return false;
			}
			resp = (new String(buffer)).toLowerCase();
			if ("y".equals(resp) || "yes".equals(resp)) {
				return true;
			}
			// if we reached here, check if it was a "no"
		} while (!"n".equals(resp) && "yes".equals(resp));

		return false;
	}

	/**
	 * Used to request some budget.
	 * 
	 * @param requestedBudget
	 * @param reason
	 * @return the requested budget, or less
	 */
	@Override
	public double requestBudget(double requestedBudget, String reason) {
		BudgetReasonDirDeptEnum budgetReason;
		try {
			budgetReason = BudgetReasonDirDeptEnum.valueOf(reason);
		} catch (IllegalArgumentException exc) {
			// the reason value is invalid; throw the exception
			throw exc;
		} catch (NullPointerException exc) {
			exc.printStackTrace();
			// something really went wrong; don't give him any budget anyway
			return 0;
		}

		return requestBudget(requestedBudget, budgetReason);
	}

	/**
	 * Used to request some budget.
	 * 
	 * @param requestedBudget
	 * @param reason
	 * @return the requested budget, or less
	 */
	public double requestBudget(double requestedBudget,
			BudgetReasonDirDeptEnum reason) {
		Double usedBudget = mUsedBudget.get(reason);
		if (usedBudget == null) {
			usedBudget = (double) 0;
		}
		Double maxBudget = MAX_BUDGET.get(reason);
		if (maxBudget == null) {
			maxBudget = (double) 0;
		}
		double availableBudget = maxBudget - usedBudget;

		double allowedBudget = availableBudget < requestedBudget ? availableBudget
				: requestedBudget;
		usedBudget += allowedBudget;
		mUsedBudget.put(reason, usedBudget);

		return allowedBudget;
	}

	/**
	 * This method calculates the bonus for which the Director is eligible
	 * 
	 * @return the bonus value
	 */
	@Override
	public int calcBonus() {
		/* As of now, this is always "0" */
		return 0;
	}

	/**
	 * Used to change the department with another Director
	 * 
	 * @param newDept
	 *            - the Departement that this Director will take over
	 * @return
	 */
	public boolean changeDepartement(Departement newDept) {
		DirecteurDepartement currentDirector;
		if (newDept == null
				|| (currentDirector = newDept.getDirecteurDepartement()) == null) {
			return false;
		}

		return exchangeDepartments(this, currentDirector);
	}

	/**
	 * Overrides the setter of the father (Employe) method. Will exchange the
	 * Departments with current director, if possible.
	 */
	@Override
	public void setDepartement(Departement dept) {
		if (!changeDepartement(dept)) {
			super.setDepartement(dept);
		}
	}

	/**
	 * Will the Director give the approval to start this project?
	 * 
	 * @param p
	 * @return
	 */
	public boolean donnerAccord(Projet p) {
		/* First of all we'll check if we have the budget */
		if (!this.hasBudget(p.getBudjetProjet())) {
			// We don't have anymore budget; deny the approval
			return false;
		}
		// OK, we have some budget; we'll ask the user (Director?) if he wants
		// to approve it
		StringBuffer msgBuf = new StringBuffer("On demande ");
		msgBuf.append(p.getBudjetProjet());
		msgBuf.append(" EUR pour le projet ");
		msgBuf.append(p.getThemeProjet());
		msgBuf.append("\nDonner l'accord? (Y/N) ");
		if (askYesNo(msgBuf.toString())) {
			double approvedBudget = this.requestBudget(p.getBudjetProjet(),
					BudgetReasonDirDeptEnum.PROJECT);
			// Check if we got enough budget
			if (approvedBudget < p.getBudjetProjet()) {
				/* We can't allocate the enough budget for this project; ask */
				/* reuse the same variables */
				msgBuf = new StringBuffer(
						"Le budget n'est pas souffisant. On peut alloquer seulement ");
				msgBuf.append(approvedBudget);
				msgBuf.append("\nContinuer avec un budget insouffisant? (Y/N) ");
				if (askYesNo(msgBuf.toString())) {
					return true;
				} else {
					/* No approval given; return the budget back */
					this.addBudget(approvedBudget);
					return false;
				}
			}
		}
		/* The approval wasn't given */
		return false;
	}
}
